﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Criterion;
using NHibernate.Persister.Entity;
using ProjectBase.Core;

namespace $safeprojectname$.NH
{
    /// <summary>
    ///     Base powerfull class for every Dao.
    ///     Every usefull method which can help with handling entity
    ///     should be placed here (e.g. GetAll())
    /// </summary>
    /// <typeparam name = "TEntity">entity</typeparam>
    /// <typeparam name = "TID"></typeparam>
    /// <typeparam name = "TFilter"></typeparam>
    public abstract class BaseDao<TEntity, TID, TFilter>
        : NHGenericDao<TEntity, TID>
        , IEntityDao<TEntity, TID, TFilter>
        , IApplyFilterRestrictions<TFilter, TID>
        where TEntity : class, IPersistentObjectWithTypedId<TID>
        where TFilter : class, IFilter<TID>
    {
        /// <summary>
        ///     The Expression entity template,
        ///     which could be used for extension methods as the 'property marker'
        /// </summary>
        protected virtual TEntity Entity { get; set; }

        #region IEntityDao<T,TID,TFilter> Members

        public abstract IEnumerable<TEntity> Find(TFilter filter);

        #endregion IEntityDao<T,TID,TFilter> Members

        /// <summary>
        ///     Uniqueness Check - Sometimes is useful to filter for existing entities
        ///     which are different from current entity (add or update)
        ///     Appends condition entity.ID &lt;&gt; exceptID
        /// </summary>
        /// <param name = "filter">Filtering parameters</param>
        /// <param name = "exceptID">The ID which should be excepted from the filtered list</param>
        /// <returns>The filtered list without entity of provided ID</returns>
        public virtual IEnumerable<TEntity> Find(TID exceptID, TFilter filter)
        {
            Criteria.Add(Restrictions.Not(Restrictions.Eq(Constants.Common.ID, exceptID)));
            filter.ProjectionList[Constants.Common.ID] = Constants.Common.ID;
            return Find(filter);
        }

        #region Merge

        public virtual TEntity ReAttach(TEntity entity)
        {
            return Session.Merge(entity);
        }

        #endregion Merge

        #region property Map

        private IList<MappingDescription> _mappingInfo;

        protected virtual IList<MappingDescription> ReadMappingInfo()
        {
            var entityType = typeof(TEntity);
            var factory = NHSessionFactory.GetFactory(SessionFactoryName);
            var persister = factory.GetClassMetadata(entityType) as AbstractEntityPersister;

            if (persister.IsNull())
            {
                throw new InvalidCastException("NHibernate ISessionFactory did not return 'AbstractEntityPersister'  on GetClassMetadata()");
            }

            var propertyNameList = persister.PropertyNames;

            var map = new List<MappingDescription>();
            map.Add(new MappingDescription
            {
                PropertyName = Constants.Common.ID,
                PersistenceName = persister.KeyColumnNames.First(),
                PropertyInfo = entityType.GetProperty(Constants.Common.ID),
            });

            foreach (var propertyName in propertyNameList)
            {
                var index = persister.GetPropertyIndex(propertyName);

                var description = new MappingDescription
                {
                    PropertyName = propertyName,
                    PersistenceName = persister.GetPropertyColumnNames(propertyName).First(),
                    PropertyInfo = entityType.GetProperty(propertyName),
                    CanInsert = persister.PropertyInsertability[index],
                    CanUpdate = persister.PropertyUpdateability[index],
                };

                map.Add(description);
            }
            return map;
        }

        public virtual IList<MappingDescription> MappingInfo
        {
            get
            {
                var mappingInfo = _mappingInfo;
                if (mappingInfo.IsNull())
                {
                    mappingInfo = ReadMappingInfo();
                    _mappingInfo = mappingInfo;
                }
                return mappingInfo;
            }
        }

        #endregion property Map

        #region IApplyFilterRestrictions

        IList<ICriterion> IApplyFilterRestrictions.Apply(IFilter filter)
        {
            if (filter is TFilter)
            {
                return Apply(filter as TFilter);
            }
            return null;
        }

        IList<ICriterion> IApplyFilterRestrictions<TFilter, TID>.Apply(TFilter filter)
        {
            return Apply(filter);
        }

        protected virtual IList<ICriterion> Apply(TFilter filter)
        {
            var restrictions = new List<ICriterion>();
            return restrictions;
        }

        #endregion IApplyFilterRestrictions
    }
}